#include "server.h"
#include "server_helper.h"
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <websocketpp/common/thread.hpp>
#include <websocketpp/frame.hpp>
#include <nlohmann/json.hpp>
#include "util.h"
#include "constant.h"
#include <glog/logging.h>
#include <nlohmann/json.hpp>
#include <iostream>
using namespace nlohmann;
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::bind;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;

using websocketpp::lib::condition_variable;
using websocketpp::lib::lock_guard;
using websocketpp::lib::mutex;
using websocketpp::lib::thread;
using websocketpp::lib::unique_lock;
typedef websocketpp::frame::opcode::value opcode_value;
#define LOG_USER_ID(u) "user(" << u->id << ")"
namespace dymy
{
    enum action_type
    {
        SUBSCRIBE,
        UNSUBSCRIBE,
        MESSAGE
    };

    struct action
    {
        action(action_type t, connection_hdl h) : type(t), hdl(h) {}

        action(action_type t, connection_hdl h, server::message_ptr m)
            : type(t), hdl(h), msg(m) {}

        action_type type;
        websocketpp::connection_hdl hdl;
        server::message_ptr msg;
    };
    std::ostream &operator<<(std::ostream &os, const dymy::card_t &c)
    {
#ifdef CARD_RELAX_MODE
        return os << static_cast<int>(c);
#else
        return os << static_cast<int>(c) << "(" << static_cast<int>(dymy::card::decode_card_num(c)) << ")";
#endif
    }
    std::ostream &operator<<(std::ostream &os, const std::vector<card_t> &cards)
    {
        for (auto begin = cards.cbegin(); begin != cards.cend(); ++begin)
        {
            os << *begin << ' ';
        }
        return os;
    }

    std::ostream &operator<<(std::ostream &os, const multiple_card_t &cards)
    {
        for (auto begin = cards.cbegin(); begin != cards.cend(); ++begin)
        {
            os << *begin << ' ';
        }
        return os;
    }

    class Server::ServerImpl
    {
    public:
        typedef std::weak_ptr<User> con_lisst_value_type;
        typedef std::map<connection_hdl, con_lisst_value_type, std::owner_less<connection_hdl>> con_list;
        typedef bool (*should_notify_callback)(const User *u);

    protected:
        std::shared_ptr<UserManager> m_user_manager;
        std::unique_ptr<TableManager> m_table_manager;

    private:
        server m_server;
        con_list m_connections;
        std::queue<action> m_actions;

        mutex m_action_lock;
        condition_variable m_action_cond;

    private:
        bool on_validate(connection_hdl hdl);

    protected:
        void on_open(connection_hdl hdl);
        void on_close(connection_hdl hdl);
        void on_message(connection_hdl hdl, server::message_ptr msg);

    private:
        //    void do_rep_successful_user(nlohmann::json &j, unsigned int id, const connection_hdl &hdl) ;
        void rep_to_table_users(nlohmann::json &j, ID_t table_id, std::initializer_list<ID_t> extra_other_id);
        void rep_to_online_users(nlohmann::json &j, should_notify_callback callback = nullptr);
        // void _fill_table_info(nlohmann::json::object_t &j_data, CardTable *&t);
        inline void handle_message(const connection_hdl &hdl, const std::string &payload);

        inline decltype(auto) log(decltype(LOG(INFO)) &&lg, ID_t table_id, size_t index, const std::shared_ptr<User> &u);

    public:
        ServerImpl();
        inline void run(uint16_t port);
        inline void release();
        void process_messages();

    protected:
        void set_rep_code_msg_to_json(nlohmann::json &j, const int &code, const std::string &msg, const connection_hdl &hdl);

        //    nlohmann::json on_req_register(const nlohmann::json &req_data, const connection_hdl &hdl) ;
        //    nlohmann::json on_req_login(const nlohmann::json &req_data, const connection_hdl &hdl) ;
        //    nlohmann::json on_req_reset_password(const nlohmann::json &req_data, const connection_hdl &hdl) ;
        void on_user_offline(std::shared_ptr<User> &action_user, size_t index);
        void on_req_add_person(const json &a_req_data, const connection_hdl &hdl);

        void on_req_prepared(const nlohmann::json &req_data, const connection_hdl &hdl);

        void on_req_rob_landlord(const nlohmann::json &req_data, const connection_hdl &hdl);
        void on_req_play_cards(const nlohmann::json &req_data, const connection_hdl &hdl);
        void on_req_server_info(const nlohmann::json &req_data, const connection_hdl &hdl);
        void on_req_table_info(const nlohmann::json &req_data, const connection_hdl &hdl);

        void on_req_chat_info(const nlohmann::json &req_data, const connection_hdl &hdl);
        void on_req_table_chat_info(const nlohmann::json &req_data, const connection_hdl &hdl);

        inline void on_connection_closed(const connection_hdl &hdl);
        inline void dispatch_conn_open(connection_hdl &hdl);
        inline std::shared_ptr<User> find_user_from_connection(const connection_hdl &hdl);
    };

    Server::Server() : impl_(std::make_unique<ServerImpl>())
    {
    }
    Server::Server(Server &&) = default;
    Server &Server::operator=(Server &&) = default;
    void Server::run(uint16_t port)
    {
        impl_->run(port);
    }
    void Server::release()
    {
        impl_->release();
    }
    Server::~Server() = default;
    bool Server::ServerImpl::on_validate(connection_hdl hdl)
    {
        auto user_name = m_server.get_con_from_hdl(hdl)->get_request_header("user_name");
        return user_name.length() > 0;
    }

    void Server::ServerImpl::on_open(connection_hdl hdl)
    {
        // auto pp = m_server.get_con_from_hdl(hdl);
        {
            lock_guard<mutex> guard(m_action_lock);
            m_actions.push(action(SUBSCRIBE, hdl));
        }
        m_action_cond.notify_one();
    }
    void Server::ServerImpl::on_close(connection_hdl hdl)
    {
        // auto pp = m_server.get_con_from_hdl(hdl);
        {
            lock_guard<mutex> guard(m_action_lock);
            m_actions.push(action(UNSUBSCRIBE, hdl));
        }
        m_action_cond.notify_one();
    }

    void Server::ServerImpl::on_message(connection_hdl hdl, server::message_ptr msg)
    {
        // auto pp = m_server.get_con_from_hdl(hdl);
        {
            lock_guard<mutex> guard(m_action_lock);
            m_actions.push(action(MESSAGE, hdl, msg));
        }
        m_action_cond.notify_one();
    }

    //    void do_rep_successful_user(nlohmann::json &j, unsigned int id, const connection_hdl &hdl) ;
    void Server::ServerImpl::rep_to_table_users(nlohmann::json &j, ID_t table_id, std::initializer_list<ID_t> extra_other_id)
    {
        j["rep_code"] = REP_CODE_SUCCESS;
        j["rep_message"] = REP_MESSAGE_SUCCESS;
        if (m_table_manager->exist_table(table_id))
        {
            for (size_t i = 0; i < m_table_manager->get_person_num(); ++i)
            {
                ID_t u_id = m_table_manager->find_table_user(table_id, i);
                if (u_id > 0)
                {
                    auto u = m_user_manager->get_user(u_id);
                    if (u && !u->connection.expired())
                    {
                        m_server.send(u->connection, j.dump(), opcode_value::TEXT);
                    }
                }
            }
        }

        if (extra_other_id.size() > 0)
        {
            auto begin = extra_other_id.begin();
            while (begin != extra_other_id.end())
            {
                const ID_t &user_id = *begin++;
                auto u = m_user_manager->get_user(user_id);
                if (u && !u->connection.expired())
                {
                    m_server.send(u->connection, j.dump(), opcode_value::TEXT);
                }
            }
        }
    };
    void Server::ServerImpl::rep_to_online_users(nlohmann::json &j, Server::ServerImpl::should_notify_callback callback)
    {
        j["rep_code"] = REP_CODE_SUCCESS;
        j["rep_message"] = REP_MESSAGE_SUCCESS;

        auto us = m_user_manager->get_users();
        using online_users = UserManager::online_users;
        using UserSPtr = UserManager::UserSPtr;
        online_users::iterator begin = us.begin();
        online_users::iterator end = us.end();

        if (callback)
        {
            while (begin != end)
            {
                online_users::value_type &value = *begin++;
                const ID_t &id = value.first;
                UserSPtr &u = value.second;
                if (u && !u->connection.expired() && callback(u.get()))
                {
                    m_server.send(u->connection, j.dump(), opcode_value::TEXT);
                }
            }
        }
        else
        {
            while (begin != end)
            {
                online_users::value_type &value = *begin++;
                const ID_t &id = value.first;
                UserSPtr &u = value.second;
                if (u && !u->connection.expired())
                {
                    m_server.send(u->connection, j.dump(), opcode_value::TEXT);
                }
            }
        }
    }
    // void _fill_table_info(nlohmann::json::object_t &j_data, CardTable *&t);
    inline void Server::ServerImpl::handle_message(const connection_hdl &hdl, const std::string &payload)
    {
        try
        {
            json j = json::parse(payload);
            int code = j.at("code");
            switch (code)
            {
            case REQ_TYPE_ADD_PERSON:
            {
                //                bean_req_add_person add_person;
                on_req_add_person(j.at("data"), hdl);
            }
            break;
            case REQ_TYPE_PREPARED:
            {
                //                bean_req_prepared prepared;
                on_req_prepared(j.at("data"), hdl);
            }
            break;
            case REQ_TYPE_ROB_LANDLORD:
            {
                //                bean_req_rob_landlord rob_landlord;
                on_req_rob_landlord(j.at("data"), hdl);
            }
            break;
            case REQ_TYPE_PLAY_CARDS:
            {
                //                bean_req_play_cards play_cards;
                on_req_play_cards(j.at("data"), hdl);
            }
            break;
            case REQ_TYPE_SERVER_INFO:
            {
                on_req_server_info(j.at("data"), hdl);
            }
            break;
            case REQ_TYPE_TABLE_INFO:
            {
                on_req_table_info(j.at("data"), hdl);
            }
            break;
            case REQ_TYPE_CHAT:
            {
                on_req_chat_info(j.at("data"), hdl);
            }
            break;
            case REQ_TYPE_CHAT_TABLE:
            {
                on_req_table_chat_info(j.at("data"), hdl);
            }
            break;
                //
                //            default:
                //
                //                break;
            }
        }
        catch (const json::parse_error &err)
        {
            std::cerr << "handle message json parse error:" << err.what() << std::endl;
        }
        catch (...)
        {
            std::cerr << "handle message other error" << std::endl;
        }
    }
    inline decltype(auto) Server::ServerImpl::log(decltype(LOG(INFO)) &&lg, ID_t table_id, size_t index, const std::shared_ptr<User> &u)
    {
        return lg << "(" << table_id << "-" << index << "-" << (u ? u->id : 0) << ")";
    }

    Server::ServerImpl::ServerImpl() : m_user_manager(std::make_shared<UserManager>()), m_table_manager(std::make_unique<TableManager>())
    {
        m_server.init_asio();
        m_server.set_access_channels(websocketpp::log::alevel::none);
        m_server.set_validate_handler(bind(&ServerImpl::on_validate, this, ::_1));
        m_server.set_open_handler(bind(&ServerImpl::on_open, this, ::_1));
        m_server.set_close_handler(bind(&ServerImpl::on_close, this, ::_1));
        m_server.set_message_handler(bind(&ServerImpl::on_message, this, ::_1, ::_2));
    };
    void Server::ServerImpl::run(uint16_t port)
    {
        std::thread t(bind(&ServerImpl::process_messages, this));

        m_server.listen(port);

        m_server.start_accept();
        try
        {
            m_server.run();
        }
        catch (const std::exception &e)
        {
            LOG(FATAL) << e.what();
            std::cerr << "websocketpp run error:" << e.what() << std::endl;
        }
        t.join();
    }
    void Server::ServerImpl::release()
    {
        m_server.stop_listening();
    }
    void Server::ServerImpl::process_messages()
    {
        while (1)
        {
            unique_lock<mutex> lock(m_action_lock);

            while (m_actions.empty())
            {
                m_action_cond.wait(lock);
            }

            action a = m_actions.front();
            m_actions.pop();

            lock.unlock();

            if (a.type == SUBSCRIBE)
            {
                dispatch_conn_open(a.hdl);
                std::cout << "subscribe size:" << m_connections.size() << std::endl;
            }
            else if (a.type == UNSUBSCRIBE)
            {
                on_connection_closed(a.hdl);
                std::cout << "unsubscribe size:" << m_connections.size() << std::endl;
            }
            else if (a.type == MESSAGE)
            {
                handle_message(a.hdl, a.msg->get_payload());
            }
            else
            {
                // undefined.
                break;
            }
        }
    }

    void Server::ServerImpl::set_rep_code_msg_to_json(nlohmann::json &j, const int &code, const std::string &msg, const connection_hdl &hdl)
    {
        j["rep_code"] = code;
        j["rep_message"] = msg;
        m_server.send(hdl, j.dump(), opcode_value::TEXT);
    };

    //    nlohmann::json on_req_register(const nlohmann::json &req_data, const connection_hdl &hdl) ;
    //    nlohmann::json on_req_login(const nlohmann::json &req_data, const connection_hdl &hdl) ;
    //    nlohmann::json on_req_reset_password(const nlohmann::json &req_data, const connection_hdl &hdl) ;
    void Server::ServerImpl::on_user_offline(std::shared_ptr<User> &action_user, size_t index)
    {
        json::object_t req_data;
        const ID_t &id = action_user->last_online_table_id;
        req_data["id"] = id;
        req_data["index"] = index;
        req_data["action"] = 0;
        log(LOG(INFO), id, index, action_user) << "on_user_offline";
        int r = m_table_manager->join_or_quit_table(action_user, id, index, 0);
        json j;
        j["code"] = REP_TYPE_ADD_PERSON;
        if (r == 1)
        {
            json::object_t j_data = json::object();

            j_data["action_params"] = req_data;
            j_data["action_result"] = r;

            json j_user_info =
                {
                    {"id", action_user->id},
                    {"name", action_user->user_name},
                };
            j_data["action_user"] = j_user_info;
            m_table_manager->fill_table_info(j_data, id);
            j["data"] = j_data;
            rep_to_table_users(j, id, {});

            json notify_j;
            notify_j["code"] = REP_TYPE_PERSON_CHANGED_NOTIFY;
            notify_j["data"] = j_data;
            rep_to_online_users(notify_j, [](const User *u)
                                { return u->notify; });
        }
    }

    void Server::ServerImpl::on_req_add_person(const json &a_req_data, const connection_hdl &hdl)
    {
        auto action_user = find_user_from_connection(hdl);
        json req_data = a_req_data;
        const ID_t id = req_data["id"];

        // const ID_t user_id = req_data["user_id"];
        ssize_t index = req_data["index"];
        const int action = req_data["action"];

        log(LOG(INFO), id, index, action_user) << "on_req_add_person->action:" << action;

        if (action && index < 0)
        {
            //自动分配位置
            index = m_table_manager->find_free_position(id);
            req_data["index"] = index;
        }

        int r = m_table_manager->join_or_quit_table(action_user, id, index, action);
        json j;
        j["code"] = REP_TYPE_ADD_PERSON;

        switch (r)
        {
        case -2000:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_USER_NAME, REP_MESSAGE_ERROR_INVALID_USER_NAME, hdl);
            break;
        case -2001:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_TABLE_ID, REP_MESSAGE_ERROR_INVALID_TABLE_ID, hdl);
            break;
        case -2002:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_TABLE_INDEX, REP_MESSAGE_ERROR_INVALID_TABLE_INDEX, hdl);
            break;
        case -2003:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_INDEX_BUSY, REP_MESSAGE_ERROR_TABLE_INDEX_BUSY, hdl);

            break;
        case -2004:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_ALREADY_JOIN, REP_MESSAGE_ERROR_TABLE_ALREADY_JOIN, hdl);

            break;
        case -2006:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_NOT_JOIN, REP_MESSAGE_ERROR_TABLE_NOT_JOIN, hdl);

            break;
        case -2008:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_REPEAT_JOIN, REP_MESSAGE_ERROR_TABLE_REPEAT_JOIN, hdl);

            break;
        case ERROR_PROCESS:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_INVALID_PROCESS, REP_MESSAGE_ERROR_TABLE_INVALID_PROCESS,
                                     hdl);
            break;
        case 1:
        {
            json::object_t j_data = json::object();

            j_data["action_params"] = req_data;
            j_data["action_result"] = r;

            // auto action_user = m_user_manager->get_user(user_id);

            json j_user_info =
                {
                    {"id", action_user->id},
                    {"user_name", action_user->user_name},
                    {"name", action_user->user_name},
                };
            j_data["action_user"] = j_user_info;
            m_table_manager->fill_table_info(j_data, id);
            j["data"] = j_data;
            if (action)
            {
                rep_to_table_users(j, id, {});
            }
            else
            {
                //如果退出牌桌，牌桌上的这个用户被清掉，无法回调，需要将这个用户的id传进去，额外回调一次
                rep_to_table_users(j, id, {action_user->id});
            }

            json notify_j;
            notify_j["code"] = REP_TYPE_PERSON_CHANGED_NOTIFY;
            notify_j["data"] = j_data;
            rep_to_online_users(notify_j, [](const User *u)
                                { return u->notify; });
        }
        break;

        default:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_UNKNOW, REP_MESSAGE_ERROR_UNKNOW, hdl);
            break;
        }
    };

    void Server::ServerImpl::on_req_prepared(const nlohmann::json &req_data, const connection_hdl &hdl)
    {
        auto action_user = find_user_from_connection(hdl);

        const ID_t id = req_data["id"];
        // const ID_t user_id = req_data["user_id"];
        const ssize_t index = req_data["index"];
        const int action = req_data["action"];
        log(LOG(INFO), id, index, action_user) << "on_req_prepared->action:" << action;

        int r = m_table_manager->prepared_table(action_user, id, index, action);
        json j;
        j["code"] = REP_TYPE_PREPARED;
        switch (r)
        {
        case -2000:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_USER_NAME, REP_MESSAGE_ERROR_INVALID_USER_NAME, hdl);
            break;
        case -2001:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_TABLE_ID, REP_MESSAGE_ERROR_INVALID_TABLE_ID, hdl);

            break;
        case -2002:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_TABLE_INDEX, REP_MESSAGE_ERROR_INVALID_TABLE_INDEX, hdl);

            break;
        case -2005:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_ALREADY_PREPARED,
                                     REP_MESSAGE_ERROR_TABLE_ALREADY_PREPARED, hdl);
            break;
        case -2007:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_ALREADY_NOT_PREPARED,
                                     REP_MESSAGE_ERROR_TABLE_ALREADY_NOT_PREPARED, hdl);
            break;
        case ERROR_EMPTY_PERSON:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_NOT_EXISTS_USER, REP_MESSAGE_ERROR_TABLE_NOT_EXISTS_USER,
                                     hdl);
            break;
        case ERROR_PROCESS:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_INVALID_PROCESS, REP_MESSAGE_ERROR_TABLE_INVALID_PROCESS,
                                     hdl);
            break;
        case 1:
        case 0:
        {
            json::object_t j_data = json::object();
            j_data["action_params"] = req_data;
            j_data["action_result"] = r;

            // auto action_user = m_user_manager->get_user(user_id);

            json j_user_info =
                {
                    {"id", action_user->id},
                    {"user_name", action_user->user_name},
                    {"name", action_user->user_name},
                };
            j_data["action_user"] = j_user_info;
            if (m_table_manager->try_start(id))
            {
                LOG(INFO) << "hole:"
                          << "(" << id << ")" << m_table_manager->find_table_hole_cards(id);

                for (size_t i = 0; i < m_table_manager->get_person_num(); ++i)
                {
                    LOG(INFO) << "cards:"
                              << "(" << id << "-" << i << ")" << m_table_manager->find_table_user_cards(id, i);
                }
            }
            m_table_manager->fill_table_info(j_data, id);
            // table_ptr table = m_table_manager->get_table(static_cast<uint32_t>(id));
            // if (table != nullptr)
            // {
            //     if (table->start() == 1)
            //     {
            //     }

            //     _fill_table_info(j_data, table);
            // }

            j["data"] = j_data;
            rep_to_table_users(j, id, {});
        }
        break;
        default:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_UNKNOW, REP_MESSAGE_ERROR_UNKNOW, hdl);

            break;
        }
    }

    void Server::ServerImpl::on_req_rob_landlord(const nlohmann::json &req_data, const connection_hdl &hdl)
    {
        auto action_user = find_user_from_connection(hdl);

        const ID_t id = req_data["id"];
        // const ID_t user_id = req_data["user_id"];
        const ssize_t index = req_data["index"];
        const int action = req_data["action"];
        log(LOG(INFO), id, index, action_user) << "on_req_rob_landlord->action:" << action;

        int r = m_table_manager->rob_landlord_table(action_user, id, index, action);
        json j;
        j["code"] = REP_TYPE_ROB_LANDLORD;
        switch (r)
        {
        case -2000:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_USER_NAME, REP_MESSAGE_ERROR_INVALID_USER_NAME, hdl);
            break;
        case -2001:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_TABLE_ID, REP_MESSAGE_ERROR_INVALID_TABLE_ID, hdl);
            break;
        case -2002:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_TABLE_INDEX, REP_MESSAGE_ERROR_INVALID_TABLE_INDEX, hdl);
            break;
        case ERROR_EMPTY_PERSON:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_NOT_EXISTS_USER, REP_MESSAGE_ERROR_TABLE_NOT_EXISTS_USER,
                                     hdl);
            break;
        case ERROR_PROCESS:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_INVALID_PROCESS, REP_MESSAGE_ERROR_TABLE_INVALID_PROCESS,
                                     hdl);
            break;
        case ERROR_NOT_SELF:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_NOT_SELF, REP_MESSAGE_ERROR_TABLE_NOT_SELF, hdl);
            break;
        case 1:
        case 0:
        case 2:
        {
            json::object_t j_data = json::object();
            j_data["action_params"] = req_data;
            j_data["action_result"] = r;

            // auto action_user = m_user_manager->get_user(user_id);
            json j_user_info =
                {
                    {"id", action_user->id},
                    {"user_name", action_user->user_name},
                    {"name", action_user->user_name},
                };
            j_data["action_user"] = j_user_info;

            //                * 0:不抢，1:抢地主成功 ,2:都不抢地主

            bool refresh = false;
            if (r == 0)
            {
            }
            else if (r == 1)
            {
            }
            else
            {
                //重新洗牌了
                refresh = true;
                LOG(INFO) << "hole:"
                          << "(" << id << ")" << m_table_manager->find_table_hole_cards(id);

                for (size_t i = 0; i < m_table_manager->get_person_num(); ++i)
                {
                    LOG(INFO) << "cards:"
                              << "(" << id << "-" << i << ")" << m_table_manager->find_table_user_cards(id, i);
                }
            }

            m_table_manager->fill_table_info(j_data, id);
            j_data["refresh"] = refresh;

            j["data"] = j_data;
            rep_to_table_users(j, id, {});
        }
        break;

        default:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_UNKNOW, REP_MESSAGE_ERROR_UNKNOW, hdl);

            break;
        }
    }
    void Server::ServerImpl::on_req_play_cards(const nlohmann::json &req_data, const connection_hdl &hdl)
    {
        auto action_user = find_user_from_connection(hdl);

        const ID_t id = req_data["id"];
        // const ID_t user_id = req_data["user_id"];
        const ssize_t index = req_data["index"];
        const std::vector<card_t> cards = req_data["cards"];
        log(LOG(INFO), id, index, action_user) << "on_req_play_cards->cards:" << cards;

        int r = m_table_manager->play_cards_table(action_user, id, index, cards);
        json j;
        j["code"] = REP_TYPE_PLAY_CARDS;
        switch (r)
        {
        case -2000:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_USER_NAME, REP_MESSAGE_ERROR_INVALID_USER_NAME, hdl);
            break;
        case -2001:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_TABLE_ID, REP_MESSAGE_ERROR_INVALID_TABLE_ID, hdl);
            break;
        case -2002:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_INVALID_TABLE_INDEX, REP_MESSAGE_ERROR_INVALID_TABLE_INDEX, hdl);
            break;
        case ERROR_EMPTY_PERSON:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_NOT_EXISTS_USER, REP_MESSAGE_ERROR_TABLE_NOT_EXISTS_USER,
                                     hdl);
            break;
        case ERROR_PROCESS:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_INVALID_PROCESS, REP_MESSAGE_ERROR_TABLE_INVALID_PROCESS,
                                     hdl);
            break;
        case ERROR_NOT_SELF:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_NOT_SELF, REP_MESSAGE_ERROR_TABLE_NOT_SELF, hdl);
            break;
        case ERROR_CARDS:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_INVALID_CARDS, REP_MESSAGE_ERROR_TABLE_INVALID_CARDS, hdl);
            break;
        case 1:
        case 0:
        case 2:
        {
            json::object_t j_data = json::object();
            j_data["action_params"] = req_data;
            j_data["action_result"] = r;

            // auto action_user = m_user_manager->get_user(user_id);
            json j_user_info =
                {
                    {"id", action_user->id},
                    {"user_name", action_user->user_name},
                    {"name", action_user->user_name},
                };
            j_data["action_user"] = j_user_info;

            m_table_manager->fill_table_info(j_data, id);
            j["data"] = j_data;
            rep_to_table_users(j, id, {});
        }
        break;
        default:
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_UNKNOW, REP_MESSAGE_ERROR_UNKNOW, hdl);
            break;
        }
    }
    void Server::ServerImpl::on_req_server_info(const nlohmann::json &req_data, const connection_hdl &hdl)
    {
        auto action_user = find_user_from_connection(hdl);

        // const ID_t user_id = req_data["user_id"];
        const bool notify = req_data["notify"];
        json j;
        j["code"] = REP_TYPE_SERVER_INFO;
        // auto u = m_user_manager->get_user(user_id);
        if (action_user)
        {
            if (notify)
            {
                action_user->notify = 1;
            }
            else
            {
                action_user->notify = 0;
            }
        }

        json::array_t j_data;
        m_table_manager->fill_server_info(j_data);
        j["data"] = j_data;
        set_rep_code_msg_to_json(j, REP_CODE_SUCCESS, REP_MESSAGE_SUCCESS, hdl);
    }
    void Server::ServerImpl::on_req_table_info(const nlohmann::json &req_data, const connection_hdl &hdl)
    {
        auto action_user = find_user_from_connection(hdl);

        // const ID_t user_id = req_data["user_id"];
        const ID_t table_id = req_data["id"];
        json j;
        j["code"] = REP_TYPE_TABLE_INFO;

        json::object_t j_data;
        m_table_manager->fill_table_info(j_data, table_id);

        j["data"] = j_data;
        set_rep_code_msg_to_json(j, REP_CODE_SUCCESS, REP_MESSAGE_SUCCESS, hdl);
    }

    void Server::ServerImpl::on_req_chat_info(const nlohmann::json &req_data, const connection_hdl &hdl)
    {
        auto action_user = find_user_from_connection(hdl);

        // const ID_t user_id = req_data["user_id"];
        json::object_t j_content = req_data["content"];
        // auto u = m_user_manager->get_user(user_id);

        json j_data;
        //    time_t tt;
        //    get_time(tt);

        time_t time = get_time();

        //    tm *t = gmtime(&tt);
        j_content["time"] = time;
        j_data["user_id"] = action_user->id;
        j_data["name"] = action_user->user_name;
        j_data["content"] = j_content;
        json j;
        j["code"] = REP_TYPE_CHAT;
        j["data"] = j_data;

        rep_to_online_users(j);
    }
    void Server::ServerImpl::on_req_table_chat_info(const nlohmann::json &req_data, const connection_hdl &hdl)
    {
        auto action_user = find_user_from_connection(hdl);

        // const ID_t user_id = req_data["user_id"];
        //req_data.find()
        auto it = req_data.find("table_id");
        //const std::string& content = req_data["content"];
        //const long& tag = req_data["timestamp"];

        json::object_t j_content = req_data["content"];

        // auto u = m_user_manager->get_user(user_id);

        json j;
        j["code"] = REQ_TYPE_CHAT_TABLE;

        if (it != req_data.end() && m_table_manager->contains_user_in_table(*it, action_user->id))
        {
            const ID_t &table_id = *it;

            json j_data;

            //        time_t tt;
            //        get_time(tt);
            time_t time = get_time();

            j_content["time"] = time;
            j_data["user_id"] = action_user->id;
            j_data["name"] = action_user->user_name;
            j_data["content"] = j_content;
            j_data["table_id"] = table_id;

            j["data"] = j_data;
            rep_to_table_users(j, table_id, {});
        }
        else
        {
            set_rep_code_msg_to_json(j, REP_CODE_ERROR_TABLE_NOT_JOIN, REP_MESSAGE_ERROR_TABLE_NOT_JOIN, hdl);
        }
    }

    inline void Server::ServerImpl::on_connection_closed(const connection_hdl &hdl)
    {
        con_list::iterator it = m_connections.find(hdl); //开始找这个用户
        if (it != m_connections.end())
        {
            auto u = it->second.lock();
            if (u)
            {
                u->connection.reset(); //先将用户的连接清空，否则vs环境下，还是会向这个已经断开的用户连接;
                std::cout << "websocket closed,the user has login successed,delete the user's connection." << LOG_USER_ID(u) << "->" << hdl.lock().get() << std::endl;
                if (u->last_online_table_id > 0)
                {
                    ssize_t table_index = m_table_manager->find_user_index(u->last_online_table_id, u->id);
                    if (table_index >= 0)
                    {
                        //模拟用户退出房间
                        on_user_offline(u, table_index);
                    }
                }
                m_user_manager->logout(u->id);
            }
            //不要忘记从连接池中清除
            m_connections.erase(it);
        }
        else
        {
            std::cout << "websocket disconnected,can not find it in the connections,it may login from other device." << hdl.lock().get() << std::endl;
        }
    }
    inline void Server::ServerImpl::dispatch_conn_open(connection_hdl &hdl)
    {
        auto pp = m_server.get_con_from_hdl(hdl);
        std::string user_name = pp->get_request_header("user_name");

        std::shared_ptr<User> u = m_user_manager->login(user_name);

        if (!u->connection.expired())
        {
            std::cout << "login mutiple,connection closed.old conn:" << u->connection.lock().get() << '.' << LOG_USER_ID(u) << "->" << hdl.lock().get() << std::endl;

            //todo 异地登录，关闭之前登录的连接；如果在一台设备上登录两次需不需要关闭之前的连接？？？
            //todo 如果之前在牌桌里，重新登陆了，需不需要通知其他人这个人离开了房间？？？
            m_server.close(u->connection, REP_CODE_ERROR_LOGIN_REPLACE, REP_MESSAGE_ERROR_LOGIN_REPLACE);
            //            m_server.send(user->connection,)

            m_connections.erase(u->connection);
            u->connection.reset();
            ID_t table_id = u->last_online_table_id;
            ID_t user_id = u->id;
            if (table_id > 0)
            {
                ssize_t index = m_table_manager->stop_table_if_user_quit(table_id, user_id);
                if (index >= 0)
                {
                    std::cout << "login mutiple,user in table,stop game." << LOG_USER_ID(u) << "->" << hdl.lock().get() << std::endl;
                    on_user_offline(u, index);
                }
            }
        }
        u->connection = hdl;
        // LOG(INFO) << "login user(" << user_id << ")";

        //TODO需要寻找吗？？？
        auto it = m_connections.find(hdl);
        if (it != m_connections.end())
        {
            it->second = con_lisst_value_type(u);
        }
        else
        {
            m_connections.insert(con_list::value_type(hdl, con_lisst_value_type(u)));
        }
        std::cout << "websocket connected,it is added to the connection." << LOG_USER_ID(u) << "->" << hdl.lock().get() << std::endl;
    }
    inline std::shared_ptr<User> Server::ServerImpl::find_user_from_connection(const connection_hdl &hdl)
    {
        auto u_it = m_connections.find(hdl);
        if (u_it == m_connections.end())
        {
            return nullptr;
        }
        auto u = u_it->second;
        if (!u.expired())
        {
            return u.lock();
        }
        return nullptr;
    }

}